frac = 1.0;
} else {
if (waypointp == nullptr) {
- Fatal() << "Internal error. Attempt to project through a waypoint that doesn't exist";
+ fatal(FatalMsg() << "Internal error. Attempt to project through a waypoint that doesn't exist");
}
if (arcpt1 == nullptr) {
- Fatal() << "Internal error: Attempt to project waypoint without predecessor";
+ fatal(FatalMsg() << "Internal error: Attempt to project waypoint without predecessor");
}
dist = linedistprj(arcpt1->latitude,
global_opts.codec = QTextCodec::codecForName(CSTR(cs_name));
}
if (!global_opts.codec) {
- Fatal() << "Unsupported character set \"" << cs_name << ".";
+ fatal(FatalMsg() << "Unsupported character set \"" << cs_name << ".");
}
}
}
#include "zlib.h" // doesn't really belong here, but is missing elsewhere.
#endif
+#include <QtCore/QDebug> // for QDebug
#include <QtCore/QList> // for QList, QList<>::const_reverse_iterator, QList<>::reverse_iterator
#include <QtCore/QScopedPointer> // for QScopedPointer
#include <QtCore/QString> // for QString
};
extern const QVector<style_vecs_t> style_list;
+[[noreturn]] void fatal(QDebug& msginstance);
[[noreturn]] void fatal(const char*, ...) PRINTFLIKE(1, 2);
void is_fatal(int condition, const char*, ...) PRINTFLIKE(2, 3);
void warning(const char*, ...) PRINTFLIKE(1, 2);
*/
-#include "defs.h"
-#include <cstdio>
-#include <cstdlib>
+#include <cstdarg> // for va_end, va_list, va_start
+#include <cstdio> // for vfprintf, stderr, fflush, fprintf, stdout
+#include <cstdlib> // for exit
+
+#include "defs.h" // for Fatal, debug_print, fatal, warning
+#include "src/core/logging.h" // for FatalMsg
+
+
+[[noreturn]] void fatal(QDebug& msginstance)
+{
+ auto* myinstance = new FatalMsg;
+ myinstance->swap(msginstance);
+ delete myinstance;
+ exit(1);
+}
+
[[noreturn]] void
fatal(const char* fmt, ...)
{
+ /* flush any buffered standard output */
+ fflush(stdout);
+
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
if (crc != 0) {
Warning().nospace() << MYNAME ": Header CRC mismatch in file " << fin->name << ".";
if (!opt_recoverymode) {
- Fatal().nospace() << MYNAME ": File " << fin->name << " is corrupt. Use recoverymode option at your risk.";
+ fatal(FatalMsg().nospace() << MYNAME ": File " << fin->name << " is corrupt. Use recoverymode option at your risk.");
}
} else if (global_opts.debug_level >= 1) {
debug_print(1, MYNAME ": Header CRC verified.\n");
if (crc != 0) {
Warning().nospace() << MYNAME ": File CRC mismatch in file " << fin->name << ".";
if (!opt_recoverymode) {
- Fatal().nospace() << MYNAME ": File " << fin->name << " is corrupt. Use recoverymode option at your risk.";
+ fatal(FatalMsg().nospace() << MYNAME ": File " << fin->name << " is corrupt. Use recoverymode option at your risk.");
}
} else if (global_opts.debug_level >= 1) {
debug_print(1, MYNAME ": File CRC verified.\n");
}
}
- Fatal() << module << ": Unsupported grid (" << grid_name <<
- ". See GPSBabel help for supported grids.\n";
+ fatal(FatalMsg() << module << ": Unsupported grid (" << grid_name <<
+ ". See GPSBabel help for supported grids.\n");
return grid_unknown; /* (warnings) */
}
}
if (result < 0) {
- Fatal() << module << ": Unsupported datum (" << datum_str <<
- "). See GPSBabel help for supported datums.";
+ fatal(FatalMsg() << module << ": Unsupported datum (" << datum_str <<
+ "). See GPSBabel help for supported datums.");
}
return result;
}
gbsize_t nbytes = gbfread(tmp.data(), 1, size, file);
if (nbytes != size) {
- Fatal() << file->module << "Attempted to read " << size <<
- "bytes, but only " << nbytes << "were available.";
+ fatal(FatalMsg() << file->module << "Attempted to read " << size <<
+ "bytes, but only " << nbytes << "were available.");
}
return tmp;
}
QJsonParseError error{};
QJsonDocument document = QJsonDocument::fromJson(file_content.toUtf8(), &error);
if (error.error != QJsonParseError::NoError) {
- Fatal().nospace() << MYNAME << ": GeoJSON parse error in " << ifd->fileName() << ": " << error.errorString();
+ fatal(FatalMsg().nospace() << MYNAME << ": GeoJSON parse error in " << ifd->fileName() << ": " << error.errorString());
}
QJsonObject rootObject = document.object();
// It's a good thing 0, 0.0, 0.0.0 aren't valid gpx versions,
// normalization makes them null.
if (gpx_write_version.isNull() || (gpx_write_version < gpx_1_0)) {
- Fatal() << MYNAME ": gpx version number"
- << gpx_write_version << "not valid.";
+ fatal(FatalMsg() << MYNAME ": gpx version number"
+ << gpx_write_version << "not valid.");
}
writer->setAutoFormatting(true);
}
if (reader->hasError()) {
- Fatal() << MYNAME << "Read error:" << reader->errorString()
+ fatal(FatalMsg() << MYNAME << "Read error:" << reader->errorString()
<< "File:" << iqfile->fileName()
<< "Line:" << reader->lineNumber()
- << "Column:" << reader->columnNumber();
+ << "Column:" << reader->columnNumber());
}
}
}
if (backuproute.empty()) {
- Fatal() << MYNAME ": Found no routes or tracks to operate on.";
+ fatal(FatalMsg() << MYNAME ": Found no routes or tracks to operate on.");
}
for (const auto* rte_old : qAsConst(backuproute)) {
double npts = 0;
if (opt_time != nullptr) {
if (!timespan.has_value()) {
- Fatal() << MYNAME ": points must have valid times to interpolate by time!";
+ fatal(FatalMsg() << MYNAME ": points must have valid times to interpolate by time!");
}
// interpolate even if time is running backwards.
npts = std::abs(timespan.value()) / max_time_step;
npts = distspan / max_dist_step;
}
if (!std::isfinite(npts) || (npts >= INT_MAX)) {
- Fatal() << MYNAME ": interpolation interval too small!";
+ fatal(FatalMsg() << MYNAME ": interpolation interval too small!");
}
// Insert the required points
{
char* fm;
if ((opt_time != nullptr) && (opt_dist != nullptr)) {
- Fatal() << MYNAME ": Can't interpolate on both time and distance.";
+ fatal(FatalMsg() << MYNAME ": Can't interpolate on both time and distance.");
} else if ((opt_time != nullptr) && (opt_route != nullptr)) {
- Fatal() << MYNAME ": Can't interpolate routes on time.";
+ fatal(FatalMsg() << MYNAME ": Can't interpolate routes on time.");
} else if (opt_time != nullptr) {
max_time_step = 1000 * strtod(opt_time, nullptr); // milliseconds
if (max_time_step <= 0) {
- Fatal() << MYNAME ": interpolation time should be positve!";
+ fatal(FatalMsg() << MYNAME ": interpolation time should be positve!");
}
} else if (opt_dist != nullptr) {
max_dist_step = strtod(opt_dist, &fm);
max_dist_step *= kMilesPerKilometer;
}
if (max_dist_step <= 0) {
- Fatal() << MYNAME ": interpolation distance should be positve!";
+ fatal(FatalMsg() << MYNAME ": interpolation distance should be positve!");
}
} else {
- Fatal() << MYNAME ": No interval specified.";
+ fatal(FatalMsg() << MYNAME ": No interval specified.");
}
}
*/
-#include <clocale> // for setlocale, LC_NUMERIC, LC_TIME
-#include <csignal> // for signal, SIGINT, SIG_ERR
-#include <cstdio> // for printf, fgetc, stdin
-#include <cstring> // for strcmp
-
-#include <QtCore/QByteArray> // for QByteArray
-#include <QtCore/QChar> // for QChar
-#include <QtCore/QCoreApplication> // for QCoreApplication
-#include <QtCore/QFile> // for QFile
-#include <QtCore/QIODevice> // for QIODevice::ReadOnly
-#include <QtCore/QLocale> // for QLocale
-#include <QtCore/QStack> // for QStack
-#include <QtCore/QString> // for QString
-#include <QtCore/QStringList> // for QStringList
-#include <QtCore/QSysInfo> // for QSysInfo
-#include <QtCore/QTextCodec> // for QTextCodec
-#include <QtCore/QTextStream> // for QTextStream
-#include <QtCore/QtConfig> // for QT_VERSION_STR
-#include <QtCore/QtGlobal> // for qPrintable, qVersion, QT_VERSION, QT_VERSION_CHECK
+#include <clocale> // for setlocale, LC_NUMERIC, LC_TIME
+#include <csignal> // for signal, SIGINT, SIG_ERR
+#include <cstdio> // for printf, fflush, fgetc, fprintf, stderr, stdin, stdout
+#include <cstring> // for strcmp
+
+#include <QtCore/QByteArray> // for QByteArray
+#include <QtCore/QChar> // for QChar
+#include <QtCore/QCoreApplication> // for QCoreApplication
+#include <QtCore/QFile> // for QFile
+#include <QtCore/QIODevice> // for QIODevice::ReadOnly
+#include <QtCore/QLocale> // for QLocale
+#include <QtCore/QMessageLogContext> // for QMessageLogContext
+#include <QtCore/QStack> // for QStack
+#include <QtCore/QString> // for QString
+#include <QtCore/QStringList> // for QStringList
+#include <QtCore/QSysInfo> // for QSysInfo
+#include <QtCore/QTextCodec> // for QTextCodec
+#include <QtCore/QTextStream> // for QTextStream
+#include <QtCore/QtConfig> // for QT_VERSION_STR
+#include <QtCore/QtGlobal> // for qPrintable, qVersion, QT_VERSION, QT_VERSION_CHECK
#ifdef AFL_INPUT_FUZZING
#include "argv-fuzz-inl.h"
#endif
#include "defs.h"
-#include "cet_util.h" // for cet_convert_init, cet_convert_deinit
-#include "csv_util.h" // for csv_linesplit
-#include "filter.h" // for Filter
-#include "filter_vecs.h" // for FilterVecs
-#include "format.h" // for Format
-#include "inifile.h" // for inifile_done, inifile_init
-#include "session.h" // for start_session, session_exit, session_init
-#include "src/core/datetime.h" // for DateTime
-#include "src/core/file.h" // for File
-#include "src/core/usasciicodec.h" // for UsAsciiCodec
-#include "vecs.h" // for Vecs
+#include "cet_util.h" // for cet_convert_init, cet_convert_deinit
+#include "csv_util.h" // for csv_linesplit
+#include "filter.h" // for Filter
+#include "filter_vecs.h" // for FilterVecs
+#include "format.h" // for Format
+#include "inifile.h" // for inifile_done, inifile_init
+#include "session.h" // for start_session, session_exit, session_init
+#include "src/core/datetime.h" // for DateTime
+#include "src/core/file.h" // for File
+#include "src/core/usasciicodec.h" // for UsAsciiCodec
+#include "vecs.h" // for Vecs
#define MYNAME "main"
// be careful not to advance argn passed the end of the list, i.e. ensure argn < qargs.size()
"\n");
}
+static void MessageHandler(QtMsgType /* type */, const QMessageLogContext& /* context */, const QString& msg)
+{
+ /* flush any buffered standard output */
+ fflush(stdout);
+ fprintf(stderr, "%s\n", qPrintable(msg));
+}
+
static void
signal_handler(int sig)
{
#endif
}
+ qInstallMessageHandler(MessageHandler);
+
(void) new gpsbabel::UsAsciiCodec(); /* make sure a US-ASCII codec is available */
global_opts.objective = wptdata;
stream >> unused2;
if (stream.status() != QDataStream::Ok) {
- Fatal() << MYNAME << ": File format error on " << read_fname << ". Perhaps this isn't a Qstarz BL-1000 file";
+ fatal(FatalMsg() << MYNAME << ": File format error on " << read_fname << ". Perhaps this isn't a Qstarz BL-1000 file");
}
BL1000_POINT_TYPE type;
default:
type = BL1000_POINT_TYPE_UNKNOWN;
- Fatal() << MYNAME << ": File format error on " << read_fname << ". Unexpected value for RCR (record reason): " << rcr;
+ fatal(FatalMsg() << MYNAME << ": File format error on " << read_fname << ". Unexpected value for RCR (record reason): " << rcr);
break;
}
fix = fix_unknown;
if (type != BL1000_POINT_TYPE_UNKNOWN) {
- Fatal() << MYNAME << ": File format error on " << read_fname << ". Unexpected value for fix quality: " << fixQuality;
+ fatal(FatalMsg() << MYNAME << ": File format error on " << read_fname << ". Unexpected value for fix quality: " << fixQuality);
}
break;
// qDebug(waypoint)
if ((waypoint->latitude < -90) || (waypoint->latitude > 90)) {
- Fatal() << MYNAME << ": File format error on " << read_fname << ". Unexpected value for latitude: " << waypoint->latitude;
+ fatal(FatalMsg() << MYNAME << ": File format error on " << read_fname << ". Unexpected value for latitude: " << waypoint->latitude);
}
if ((waypoint->longitude < -180) || (waypoint->longitude > 180)) {
- Fatal() << MYNAME << ": File format error on " << read_fname << ". Unexpected value for longitude: " << waypoint->longitude;
+ fatal(FatalMsg() << MYNAME << ": File format error on " << read_fname << ". Unexpected value for longitude: " << waypoint->longitude);
}
waypoint->altitude = altitude;
{
QFile file(read_fname);
if (!file.open(QIODevice::ReadOnly)) {
- Fatal() << MYNAME << ": Error opening file " << read_fname;
+ fatal(FatalMsg() << MYNAME << ": Error opening file " << read_fname);
}
QDataStream stream(&file);
#ifndef gpsbabel_logging_h_included
#define gpsbabel_logging_h_included
-// A wrapper for QDebug that provides a sensible Warning() and Fatal()
+// A wrapper for QDebug that provides a sensible Warning() and FatalMsg()
// with convenient functions, stream operators and manipulators.
-#include <QtCore/QDebug> // for QDebug
-#include <QtCore/QFile> // for QFile
-#include <QtCore/QIODevice> // for QIODevice, QIODevice::WriteOnly
-#include <QtCore/QString> // for QString
-#include <QtCore/QTextStream> // for QTextStream
-#include <cstdio> // for stderr
-#include <cstdlib> // for exit
+#include <QtCore/QDebug> // for QDebug
+#include <QtCore/QtGlobal> // for QtCriticalMsg, QtWarningMsg
-class Warning : public QDebug {
+class Warning : public QDebug
+{
public:
- explicit Warning(bool fatal = false) : QDebug(&msg_), fatal_(fatal) {
- }
- ~Warning() {
- QFile file;
- file.open(stderr, QIODevice::WriteOnly);
- QTextStream fileStream(&file);
- fileStream << msg_ << '\n';
- file.close();
- if (fatal_) {
- exit(1);
- }
- }
-private:
- QString msg_;
- bool fatal_;
+ explicit Warning() : QDebug(QtWarningMsg) {}
};
-class Fatal : public Warning {
- public:
- Fatal() : Warning(true) {}
+/*
+ * To use a FatalMsg pass it to fatal(), e.g.
+ * fatal(FatalMsg() << "bye bye");
+ *
+ * This
+ * 1) allows the noreturn attribute on fatal to be use by analysis
+ * tools such as cppcheck.
+ * 2) allows fatal to throw an exception instead of calling exit.
+ * This could be caught by main for a cleaner exit from a fatal error.
+ */
+class FatalMsg : public QDebug
+{
+public:
+ // We don't use QtFatalMsg here because we don't want the destructor to call abort.
+ explicit FatalMsg() : QDebug(QtCriticalMsg) {}
};
#endif // gpsbabel_logging_h_included
} else if (key == "TYPE") {
filetype = qstr.toInt(&ok);
if (!ok) {
- Fatal() << MYNAME << "Unknown file type " << key;
+ fatal(FatalMsg() << MYNAME << "Unknown file type " << key);
}
switch (filetype) {
case 4: /* M9 TrackLog (Suunto Sail Manager) */
auto year = v[2].toInt();
dt = QDate(year, month, day);
} else {
- Fatal() << MYNAME << "Invalid date" << qstr;
+ fatal(FatalMsg() << MYNAME << "Invalid date" << qstr);
}
break;
}
auto sec = v[2].toInt();
tm = QTime(hour, min, sec);
} else {
- Fatal() << MYNAME << "Invalid Time" << qstr;
+ fatal(FatalMsg() << MYNAME << "Invalid Time" << qstr);
}
break;
}
case 4:
wpt->latitude = qstr.toDouble(&ok);
if (!ok) {
- Fatal() << MYNAME << "Invalid latitude" << qstr;
+ fatal(FatalMsg() << MYNAME << "Invalid latitude" << qstr);
}
break;
case 5:
wpt->longitude = qstr.toDouble(&ok);
if (!ok) {
- Fatal() << MYNAME << "Invalid longitude" << qstr;
+ fatal(FatalMsg() << MYNAME << "Invalid longitude" << qstr);
}
break;
case 6: {
gradient = 0;
if ((opt_gpstime == nullptr) != (opt_gpsdate == nullptr)) {
- Fatal() << MYNAME ": Either both or neither of the gps_date and gps_time options must be supplied!";
+ fatal(FatalMsg() << MYNAME ": Either both or neither of the gps_date and gps_time options must be supplied!");
}
gps_datetime = QDateTime();
if ((opt_gpstime != nullptr) && (opt_gpsdate != nullptr)) {
QDate gps_date = QDate::fromString(opt_gpsdate, "yyyyMMdd");
if (!gps_date.isValid()) {
- Fatal().nospace() << MYNAME ": option gps_date value (" << opt_gpsdate << ") is invalid. Expected yyyymmdd.";
+ fatal(FatalMsg().nospace() << MYNAME ": option gps_date value (" << opt_gpsdate << ") is invalid. Expected yyyymmdd.");
}
QTime gps_time = QTime::fromString(opt_gpstime, "HHmmss");
if (!gps_time.isValid()) {
gps_time = QTime::fromString(opt_gpstime, "HHmmss.z");
if (!gps_time.isValid()) {
- Fatal().nospace() << MYNAME ": option gps_time value (" << opt_gpstime << ") is invalid. Expected hhmmss[.sss]";
+ fatal(FatalMsg().nospace() << MYNAME ": option gps_time value (" << opt_gpstime << ") is invalid. Expected hhmmss[.sss]");
}
}
gps_datetime = QDateTime(gps_date, gps_time, Qt::UTC);
if (!video_time.isValid()) {
video_time = QTime::fromString(opt_videotime, "HHmmss.z");
if (!video_time.isValid()) {
- Fatal().nospace() << MYNAME ": option video_time value (" << opt_videotime << ") is invalid. Expected hhmmss[.sss].";
+ fatal(FatalMsg().nospace() << MYNAME ": option video_time value (" << opt_videotime << ") is invalid. Expected hhmmss[.sss].");
}
}
video_offset_ms = video_time.msecsSinceStartOfDay();
*consumed = 0; /* for a possible date */
return 0;
}
- Fatal() << MYNAME << ": Could not parse date string (" << str << ").\n";
+ fatal(FatalMsg() << MYNAME << ": Could not parse date string (" << str << ").\n");
}
if ((p1 > 99) || (sep[0] == '-')) { /* Y-M-D (iso like) */
*consumed = 0;
return 0; /* don't stop here */
}
- Fatal() << MYNAME << ": Could not parse date string (" << str << ").\n";
+ fatal(FatalMsg() << MYNAME << ": Could not parse date string (" << str << ").\n");
}
tm.tm_year -= 1900;
UnicsvFormat::unicsv_check_modes(bool test)
{
if (test) {
- Fatal() << MYNAME <<
- " : Invalid combination of -w, -t, -r selected. Use only one.";
+ fatal(FatalMsg() << MYNAME <<
+ " : Invalid combination of -w, -t, -r selected. Use only one.");
}
}
route_disp_all(nullptr, nullptr, unicsv_waypt_enum_cb_lambda);
break;
case posndata:
- Fatal() << MYNAME << ": Realtime positioning not supported.";
+ fatal(FatalMsg() << MYNAME << ": Realtime positioning not supported.");
}
*fout << "No" << unicsv_fieldsep;
}
if ((wpt->latitude < -90) || (wpt->latitude > 90.0))
- Fatal() << wpt->session->name
+ fatal(FatalMsg() << wpt->session->name
<< "Invalid latitude" << lat_orig << "in waypoint"
- << wpt->shortname;
+ << wpt->shortname);
if ((wpt->longitude < -180) || (wpt->longitude > 180.0))
- Fatal() << "Invalid longitude" << lon_orig << "in waypoint"
- << wpt->shortname;
+ fatal(FatalMsg() << "Invalid longitude" << lon_orig << "in waypoint"
+ << wpt->shortname);
/*
* Some input may not have one or more of these types so we
void XcsvStyle::validate_fieldmap(const field_map& fmp, bool is_output) {
if (fmp.key.isEmpty()) {
- Fatal() << MYNAME << ": xcsv style is missing" <<
- (is_output ? "output" : "input") << "field type.";
+ fatal(FatalMsg() << MYNAME << ": xcsv style is missing" <<
+ (is_output ? "output" : "input") << "field type.");
}
if (fmp.val.isNull()) {
- Fatal() << MYNAME << ": xcsv style" << fmp.key.constData() << "is missing default.";
+ fatal(FatalMsg() << MYNAME << ": xcsv style" << fmp.key.constData() << "is missing default.");
}
if (is_output && fmp.printfc.isNull()) {
- Fatal() << MYNAME << ": xcsv style" << fmp.key.constData() << "output is missing format specifier.";
+ fatal(FatalMsg() << MYNAME << ": xcsv style" << fmp.key.constData() << "output is missing format specifier.");
}
}
} else if (p == "WAYPOINT") {
style->datatype = wptdata;
} else {
- Fatal() << MYNAME << ": Unknown data type" << p;
+ fatal(FatalMsg() << MYNAME << ": Unknown data type" << p);
}
} else
if (op == "IFIELD") {
if (tokens.size() < 3) {
- Fatal() << "Invalid IFIELD line: " << tokenstr;
+ fatal(FatalMsg() << "Invalid IFIELD line: " << tokenstr);
}
// The key ("LAT_DIR") should never contain quotes.
unsigned options = 0;
// Note: simplified() has to run after split().
if (tokens.size() < 3) {
- Fatal() << "Invalid OFIELD line: " << tokenstr;
+ fatal(FatalMsg() << "Invalid OFIELD line: " << tokenstr);
}
// The key ("LAT_DIR") should never contain quotes.